﻿namespace Unity.CecilTools
{
    using Mono.Cecil;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Threading;
    using Unity.CecilTools.Extensions;

    public static class CecilUtils
    {
        [CompilerGenerated]
        private static Func<TypeDefinition, IEnumerable<TypeReference>> f_amcache0;/*<>f__am$cache0*/
        [CompilerGenerated]
        private static Func<TypeReference, TypeDefinition> f_amcache1; /*<>f__am$cache1;*/

        public static IEnumerable<TypeDefinition> AllInterfacesImplementedBy(TypeDefinition typeDefinition)
        {
            if (f_amcache0 == null)
            {
                f_amcache0 = t => t.Interfaces;
            }
            if (f_amcache1 == null)
            {
                f_amcache1 = i => i.CheckedResolve();
            }
            return Enumerable.Select<TypeReference, TypeDefinition>(Enumerable.SelectMany<TypeDefinition, TypeReference>(TypeAndBaseTypesOf(typeDefinition), f_amcache0), f_amcache1).Distinct<TypeDefinition>();
        }

        public static IEnumerable<TypeDefinition> BaseTypesOf(TypeReference typeReference)
        {
            return TypeAndBaseTypesOf(typeReference).Skip<TypeDefinition>(1);
        }

        public static TypeReference ElementTypeOfCollection(TypeReference type)
        {
            ArrayType type2 = type as ArrayType;
            if (type2 != null)
            {
                return type2.ElementType;
            }
            if (!IsGenericList(type))
            {
                throw new ArgumentException();
            }
            return ((GenericInstanceType) type).GenericArguments.Single<TypeReference>();
        }

        public static MethodDefinition FindInTypeExplicitImplementationFor(MethodDefinition interfaceMethod, TypeDefinition typeDefinition)
        {
            <FindInTypeExplicitImplementationFor>c__AnonStorey1 storey = new <FindInTypeExplicitImplementationFor>c__AnonStorey1 {
                interfaceMethod = interfaceMethod
            };
            return Enumerable.SingleOrDefault<MethodDefinition>(typeDefinition.Methods, new Func<MethodDefinition, bool>(storey.<>m__0));
        }

        public static bool IsGenericList(TypeReference type)
        {
            return ((type.Name == "List`1") && (type.SafeNamespace() == "System.Collections.Generic"));
        }

        [DebuggerHidden]
        public static IEnumerable<TypeDefinition> TypeAndBaseTypesOf(TypeReference typeReference)
        {
            return new <TypeAndBaseTypesOf>c__Iterator0 { typeReference = typeReference, <$>typeReference = typeReference, $PC = -2 };
        }

        [CompilerGenerated]
        private sealed class <FindInTypeExplicitImplementationFor>c__AnonStorey1
        {
            internal MethodDefinition interfaceMethod;

            internal bool <>m__0(MethodDefinition m)
            {
                return Enumerable.Any<MethodReference>(m.Overrides, (Func<MethodReference, bool>) (o => o.CheckedResolve().SameAs(this.interfaceMethod)));
            }

            internal bool <>m__3(MethodReference o)
            {
                return o.CheckedResolve().SameAs(this.interfaceMethod);
            }
        }

        [CompilerGenerated]
        private sealed class <TypeAndBaseTypesOf>c__Iterator0 : IEnumerable<TypeDefinition>, IEnumerator<TypeDefinition>, IEnumerable, IEnumerator, IDisposable
        {
            internal TypeDefinition $current;
            internal int $PC;
            internal TypeReference <$>typeReference;
            internal TypeDefinition <typeDefinition>__0;
            internal TypeReference typeReference;

            [DebuggerHidden]
            public void Dispose()
            {
                this.$PC = -1;
            }

            public bool MoveNext()
            {
                uint num = (uint) this.$PC;
                this.$PC = -1;
                switch (num)
                {
                    case 0:
                        break;

                    case 1:
                        this.typeReference = this.<typeDefinition>__0.BaseType;
                        break;

                    default:
                        goto Label_0072;
                }
                if (this.typeReference != null)
                {
                    this.<typeDefinition>__0 = this.typeReference.CheckedResolve();
                    this.$current = this.<typeDefinition>__0;
                    this.$PC = 1;
                    return true;
                }
                this.$PC = -1;
            Label_0072:
                return false;
            }

            [DebuggerHidden]
            public void Reset()
            {
                throw new NotSupportedException();
            }

            [DebuggerHidden]
            IEnumerator<TypeDefinition> IEnumerable<TypeDefinition>.GetEnumerator()
            {
                if (Interlocked.CompareExchange(ref this.$PC, 0, -2) == -2)
                {
                    return this;
                }
                return new CecilUtils.<TypeAndBaseTypesOf>c__Iterator0 { typeReference = this.<$>typeReference };
            }

            [DebuggerHidden]
            IEnumerator IEnumerable.GetEnumerator()
            {
                return this.System.Collections.Generic.IEnumerable<Mono.Cecil.TypeDefinition>.GetEnumerator();
            }

            TypeDefinition IEnumerator<TypeDefinition>.Current
            {
                [DebuggerHidden]
                get
                {
                    return this.$current;
                }
            }

            object IEnumerator.Current
            {
                [DebuggerHidden]
                get
                {
                    return this.$current;
                }
            }
        }
    }
}

